package com.kawins.mina.net;

import org.apache.mina.core.buffer.IoBuffer;
import org.apache.mina.core.session.IoSession;
import org.apache.mina.filter.codec.ProtocolDecoderOutput;
import org.apache.mina.filter.codec.demux.MessageDecoderAdapter;
import org.apache.mina.filter.codec.demux.MessageDecoderResult;

import java.io.BufferedReader;
import java.io.Reader;
import java.io.StringReader;
import java.net.URLDecoder;
import java.nio.charset.Charset;
import java.nio.charset.CharsetDecoder;
import java.nio.charset.CodingErrorAction;
import java.util.HashMap;
import java.util.Map;

public class HttpRequestDecoder extends MessageDecoderAdapter {
	private static final byte[] CONTENT_LENGTH = "Content-Length:".getBytes();

	private CharsetDecoder decoder = null;

	public HttpRequestDecoder() {
		this.decoder = Charset.forName("UTF-8").newDecoder();
		this.decoder.onMalformedInput(CodingErrorAction.IGNORE);
	}

	@Override
	public MessageDecoderResult decodable(IoSession session, IoBuffer in) {
		try {
			return messageComplete(in) ? MessageDecoderResult.OK : MessageDecoderResult.NEED_DATA;
		} catch (Exception ex) {
			ex.printStackTrace();
		}

		return MessageDecoderResult.NOT_OK;
	}

	@Override
	public MessageDecoderResult decode(IoSession session, IoBuffer in, ProtocolDecoderOutput out) throws Exception {
		HttpRequestMessage m = decodeBody(in);

		if (m == null) {
			return MessageDecoderResult.NEED_DATA;
		}
		out.write(m);

		return MessageDecoderResult.OK;
	}

	private boolean messageComplete(IoBuffer in) throws Exception {
		int last = in.remaining() - 1;
		if (in.remaining() < 4) {
			return false;
		}

		if ((in.get(0) == 71) && (in.get(1) == 69) && (in.get(2) == 84)) {
			return (in.get(last) == 10) && (in.get(last - 1) == 13) && (in.get(last - 2) == 10) && (in.get(last - 3) == 13);
		}
		if ((in.get(0) == 80) && (in.get(1) == 79) && (in.get(2) == 83) && (in.get(3) == 84)) {
			int eoh = -1;
			for (int i = last; i > 2; i--) {
				if ((in.get(i) == 10) && (in.get(i - 1) == 13) && (in.get(i - 2) == 10) && (in.get(i - 3) == 13)) {
					eoh = i + 1;
					break;
				}
			}
			if (eoh == -1) {
				return false;
			}
			for (int i = 0; i < last; i++) {
				boolean found = false;
				for (int j = 0; j < CONTENT_LENGTH.length; j++) {
					if (in.get(i + j) != CONTENT_LENGTH[j]) {
						found = false;
						break;
					}
					found = true;
				}
				if (!found) {
					continue;
				}
				StringBuilder contentLength = new StringBuilder();
				for (int j = i + CONTENT_LENGTH.length; j < last; j++) {
					if (in.get(j) == 13) {
						break;
					}
					contentLength.append(new String(new byte[] { in.get(j) }));
				}

				return Integer.parseInt(contentLength.toString().trim()) + eoh == in.remaining();
			}

		}

		return false;
	}

	private HttpRequestMessage decodeBody(IoBuffer in) {
		HttpRequestMessage request = null;
		try {
			request = parseRequest(new StringReader(in.getString(this.decoder)));
		} catch (Exception e) {
			e.printStackTrace();
		}
		return request;
	}

	private HttpRequestMessage parseRequest(Reader is) throws Exception {
		BufferedReader rdr = new BufferedReader(is);

		String line = rdr.readLine();
		if (line == null) {
			return null;
		}
		String[] url = line.split(" ");
		if (url.length < 3) {
			return null;
		}
		Map<String, String> headers = new HashMap<String, String>();
		String uri = URLDecoder.decode(line, "UTF8");
		headers.put("URI", uri);
		String method = url[0].toUpperCase();
		headers.put("Method", method);
		String file = URLDecoder.decode(url[1], "UTF8");
		headers.put("File", file);
		String context = null;
		int idx = file.indexOf('?');
		if (idx != -1) {
			context = file.substring(1, idx);
		} else {
			context = file.substring(1);
		}
		headers.put("Context", context);
		headers.put("Protocol", url[2]);

		while (((line = rdr.readLine()) != null) && (line.length() > 0)) {
			String[] tokens = line.split(": ");
			headers.put(tokens[0], tokens[1]);
		}

		if (method.equalsIgnoreCase("POST")) {
			int len = Integer.parseInt((String) headers.get("Content-Length"));
			char[] buf = new char[len];
			if (rdr.read(buf) == len) {
				line = String.copyValueOf(buf);
			}
		} else if (method.equalsIgnoreCase("GET")) {
			if (idx != -1) {
				line = file.substring(idx + 1);
			} else {
				line = null;
			}
		}
		Map<String, String> parameters = new HashMap<String, String>();
		if (line != null) {
			String[] match = line.split("\\&");
			for (int i = 0; i < match.length; i++) {
				String[] tokens = match[i].split("=");
				switch (tokens.length) {
				case 0:
					parameters.put(match[i], "");
					break;
				case 1:
					parameters.put(tokens[0], "");
					break;
				default:
					String name = tokens[0];
					String params = URLDecoder.decode(tokens[1].trim(), "UTF8");
					parameters.put(name, params);
				}
			}
		}
		return new HttpRequestMessage(headers, parameters);
	}

}
